package com.my.house.common.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.my.house.common.BizException;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.WeekFields;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author yangll
 * @desc 时间通用工具类
 */
public class DateUtil {

    /**
     * 中文日期格式
     */
    public final static String DATE_CHINESE_PATTERN = "yyyy年MM月dd日";

    /**
     * 中文日期格式
     */
    public final static String CHINESE_YEAR_MONTH = "yyyy年MM月";

    /**
     * 报表时间格式
     */
    public final static String MONTH_DAY_MINUTE = "MM-dd HH:mm";

    /**
     * 中文日期格式
     */
    public final static String CHINESE_MONTH_DAY = "MM月dd日 HH:mm";

    /**
     * 标准日期格式
     */
    public final static String DATE_PATTERN = "yyyy-MM-dd";

    /**
     * 短日期格式
     */
    public final static String DATE_SHORT_PATTERN = "yyyyMMdd";

    /**
     * 斜线日期格式
     */
    public final static String DATE_SLASH_PATTERN = "yyyy/MM/dd";

    /**
     * 标准日期时分秒毫秒格式
     */
    public final static String DATETIME_MILL_SECOND = "yyyy-MM-dd HH:mm:ss.SSS";

    /**
     * 标准时间格式
     */
    public final static String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";

    /**
     * 特殊的格式 针对创建订单，拼凑的最晚支付时间
     */
    public final static String DATETIME_PATTERN_CREAT_ORDER = "yyyy-MM-dd HH:mm";

    /**
     * 短日期时间格式
     */
    public final static String DATETIME_SHORT_PATTERN = "yyyyMMddHHmmss";

    /**
     * 标准月日格式
     */
    public final static String MONTH_DAY = "MM-dd";

    /**
     * 标准年小时分钟格式
     */
    public final static String HOUR_MINUTE = "HH:mm";

    /**
     * 标准年小时分钟秒格式
     */
    public final static String HOUR_MINUTE_SECOND = "HH:mm:ss";

    public final static String HOUR_MINUTE_SECOND_SHORT = "HHmmss";

    /**
     * Number of milliseconds in a standard day.
     */
    public static final long MILLIS_PER_DAY = 24 * DateUtil.MILLIS_PER_HOUR;

    /**
     * Number of milliseconds in a standard hour.
     */
    public static final long MILLIS_PER_HOUR = 60 * DateUtil.MILLIS_PER_MINUTE;

    /**
     * Number of milliseconds in a standard minute.
     */
    public static final long MILLIS_PER_MINUTE = 60 * DateUtil.MILLIS_PER_SECOND;

    /**
     * Number of milliseconds in a standard second.
     */
    public static final long MILLIS_PER_SECOND = 1000;

    /**
     * 一小时是多少分钟
     */
    public static final int MINUTES_OF_AN_HOUR = 60;

    /**
     * 一小时是多少分钟
     */
    public static final BigDecimal MINUTE_SECONDS = new BigDecimal(60);

    /**
     * 一小时是多少秒钟
     */
    public static final BigDecimal SECONDS_OF_AN_HOUR_BD = new BigDecimal(60 * 60);

    /**
     * 一天是多少秒钟
     */
    public static final BigDecimal SECONDS_OF_DAY_BD = new BigDecimal(24 * 60 * 60);

    /**
     * 一天是多少分钟
     */
    public static final BigDecimal MINUTES_OF_DAY_BD = new BigDecimal(24 * 60);

    /**
     * 一天是多少小时
     */
    public static final BigDecimal HOURS_OF_DAY_BD = new BigDecimal(24);

    /**
     * 标准年月格式
     */
    public final static String MONTH_PATTERN = "yyyy-MM";
    /**
     * 标准年月格式2
     */
    public final static String MONTH_PATTERN2 = "yyyyMM";

    /**
     * 标准年格式
     */
    public final static String YEAR_PATTERN = "yyyy";

    /**
     * 标准月日格式
     */
    public final static String MONTH_DAY_PATTERN = "MMdd";

    /**
     * 每天开始时间
     */
    public final static String TIME_START = "00:00:00";

    /**
     * 每天截止时间
     */
    public final static String TIME_END = "23:59:59";

    /**
     * 一天
     */
    private static final Integer ONE_DAY = 1;

    /**
     * 周一
     */
    private static final Integer MONDAY = 1;

    /**
     * 周日
     */
    private static final Integer SUNDAY = 7;

    private static final Integer DAY_COUNT_OF_WEEK = 7;

    /**
     * 在yyyy-MM-dd之后拼接：空格 + 00:00:00
     *
     * @param startDate
     * @return
     */
    public static String appendTimeStart(String startDate) {
        if (StrUtil.isBlank(startDate)) {
            return null;
        }
        return StrUtil.concat(false,startDate, " ", TIME_START);
    }

    /**
     * 毫秒时间戳转日期时间
     *
     * @param timestamp 时间戳
     * @return
     */
    public static LocalDateTime toLocalDateTime(Long timestamp) {
        return Instant.ofEpochMilli(timestamp).atZone(ZoneOffset.systemDefault()).toLocalDateTime();
    }

    /**
     * 秒时间戳转日期时间
     *
     * @param timestamp 时间戳
     * @return
     */
    public static LocalDateTime secondToLocalDateTime(Long timestamp) {
        return Instant.ofEpochSecond(timestamp).atZone(ZoneOffset.systemDefault()).toLocalDateTime();
    }

    /**
     * 在yyyy-MM-dd之后拼接：空格 + 23:59:59
     *
     * @param endDate
     * @return
     */
    public static String appendTimeEnd(String endDate) {
        if (StrUtil.isBlank(endDate)) {
            return null;
        }
        return StrUtil.concat(false, endDate, " ", TIME_END);
    }

    /**
     * 获取给定时间当天开始时间字符串 格式（yyyy-MM-dd hh:mm:ss）
     *
     * @param dateTime 日期时间
     * @return 开始时间日期格式字符串 （yyyy-MM-dd hh:mm:ss）
     */
    public static String getStartFormatDateTime(LocalDateTime dateTime) {
        String formatDate = dateToString(dateTime.toLocalDate(), DATE_PATTERN);
        return appendTimeStart(formatDate);
    }

    /**
     * 获取给定时间当天开始时间字符串 格式（yyyy-MM-dd hh:mm:ss）
     *
     * @param date 日期
     * @return 开始时间日期格式字符串 （yyyy-MM-dd hh:mm:ss）
     */
    public static String getStartFormatDateTime(LocalDate date) {
        String formatDate = dateToString(date, DATE_PATTERN);
        return appendTimeStart(formatDate);
    }

    /**
     * 获取给定时间当天的开始时间
     *
     * @param dateTime 日期时间
     * @return 开始日期时间
     */
    public static LocalDateTime getStartDateTime(LocalDateTime dateTime) {
        return LocalDateTime.of(dateTime.toLocalDate(), LocalTime.MIN);
    }

    /**
     * 获取给定时间当天的开始时间
     *
     * @param date 日期
     * @return 开始日期时间
     */
    public static LocalDateTime getStartDateTime(LocalDate date) {
        return LocalDateTime.of(date, LocalTime.MIN);
    }

    /**
     * 获取给定时间当天最后的时间字符串 格式（yyyy-MM-dd hh:mm:ss）
     *
     * @param dateTime 日期
     * @return 结束时间日期格式字符串 （yyyy-MM-dd hh:mm:ss）
     */
    public static String getEndFormatDateTime(LocalDateTime dateTime) {
        String formatDate = dateToString(dateTime.toLocalDate(), DATE_PATTERN);
        return appendTimeEnd(formatDate);
    }

    /**
     * 获取给定时间当天最后的时间字符串 格式（yyyy-MM-dd hh:mm:ss）
     *
     * @param date 日期
     * @return 结束时间日期格式字符串 （yyyy-MM-dd hh:mm:ss）
     */
    public static String getEndFormatDateTime(LocalDate date) {
        String formatDate = dateToString(date, DATE_PATTERN);
        return appendTimeEnd(formatDate);
    }

    /**
     * 获取给定时间当天最后的时间字符串 格式（yyyy-MM-dd hh:mm:ss）
     *
     * @param dateTime 日期
     * @return 结束时间
     */
    public static LocalDateTime getEndDateTime(LocalDateTime dateTime) {
        return LocalDateTime.of(dateTime.toLocalDate(), LocalTime.of(23, 59, 59));
    }

    /**
     * 获取给定时间当天最后的时间字符串 格式（yyyy-MM-dd hh:mm:ss）
     *
     * @param date 日期
     * @return 结束时间
     */
    public static LocalDateTime getEndDateTime(LocalDate date) {
        return LocalDateTime.of(date, LocalTime.of(23, 59, 59));
    }

    /**
     * 获取两个日期之间的所有日期 (不包含fromDate和toDate)
     *
     * @param fromDate 开始日期
     * @param toDate   结束日期
     * @return 日期列表
     */
    public static List<String> getBetweenDates(String fromDate, String toDate) {
        if (StrUtil.isBlank(fromDate) || StrUtil.isBlank(toDate)) {
            return CollUtil.newArrayList();
        }
        LocalDate startLocalDate = LocalDate.parse(fromDate, DateTimeFormatter.ofPattern(DATE_PATTERN));
        LocalDate endLocalDate = LocalDate.parse(toDate, DateTimeFormatter.ofPattern(DATE_PATTERN));
        List<LocalDate> betweenDates = getBetweenDates(startLocalDate, endLocalDate);
        if (CollUtil.isEmpty(betweenDates)) {
            return CollUtil.newArrayList();
        }

        return betweenDates.stream()
                .map(betweenDay -> dateToString(betweenDay, DATE_PATTERN))
                .collect(Collectors.toList());
    }

    /**
     * 获取两个日期之间的所有日期 (不包含fromDate和toDate)
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 日期列表
     */
    public static List<LocalDate> getBetweenDates(LocalDate startDate, LocalDate endDate) {
        if (Objects.isNull(startDate) || Objects.isNull(endDate)) {
            return CollUtil.newArrayList();
        }
        // 返回的日期集合
        List<LocalDate> days = CollUtil.newArrayList();
        if (startDate.equals(endDate)) {
            days.add(startDate);
            return days;
        }
        //日期从小到大
        LocalDate fromDate = startDate;
        LocalDate toDate = endDate;
        if (startDate.isAfter(endDate)) {
            fromDate = endDate;
            toDate = startDate;
        }

        LocalDate tempDate = fromDate.plusDays(ONE_DAY);
        while (tempDate.isBefore(toDate)) {
            days.add(tempDate);
            tempDate = tempDate.plusDays(ONE_DAY);
        }

        return days;
    }

    /**
     * 获取两个日期之间的所有日期 (包含fromDate和toDate)
     *
     * @param fromDate 开始日期
     * @param toDate   结束日期
     * @return 日期列表
     */
    public static List<String> getDurationDates(String fromDate, String toDate) {
        if (StrUtil.isBlank(fromDate) || StrUtil.isBlank(toDate)) {
            return CollUtil.newArrayList();
        }
        LocalDate startLocalDate = LocalDate.parse(fromDate, DateTimeFormatter.ofPattern(DATE_PATTERN));
        LocalDate endLocalDate = LocalDate.parse(toDate, DateTimeFormatter.ofPattern(DATE_PATTERN));
        List<LocalDate> betweenDates = getDurationDates(startLocalDate, endLocalDate);
        if (CollUtil.isEmpty(betweenDates)) {
            return CollUtil.newArrayList();
        }

        return betweenDates.stream()
                .map(betweenDay -> dateToString(betweenDay, DATE_PATTERN))
                .collect(Collectors.toList());
    }

    /**
     * 获取日期段的日期列表 （包含startDate和endDate）
     *
     * @param startDate 起始日期
     * @param endDate   结束日期
     * @return 日期列表
     */
    public static List<LocalDate> getDurationDates(LocalDate startDate, LocalDate endDate) {
        if (Objects.isNull(startDate) || Objects.isNull(endDate)) {
            return CollUtil.newArrayList();
        }
        // 返回的日期集合
        List<LocalDate> days = CollUtil.newArrayList();
        if (startDate.equals(endDate)) {
            days.add(startDate);
            return days;
        }
        //日期从小到大
        LocalDate fromDate = startDate;
        LocalDate toDate = endDate;
        if (startDate.isAfter(endDate)) {
            fromDate = endDate;
            toDate = startDate;
        }
        LocalDate tempDate = fromDate;
        while (tempDate.isBefore(toDate.plusDays(1))) {
            days.add(tempDate);
            tempDate = tempDate.plusDays(1);
        }

        return days;
    }

    /**
     * 获取日期是星期几（周日为7）。
     *
     * @param date 日期
     * @return 星期几 周日是7
     */
    public static Integer getDayOfWeek(LocalDate date) {
        Assert.notNull(date, BizException.param().getMessage());
        return date.getDayOfWeek().getValue();
    }

    /**
     * 获取某日所在星期的每一天的集合
     * @param date 日期
     * @return 周一到周日的日期集合
     */
    public static List<LocalDate> getDaysOfWeek(LocalDate date) {
        if (Objects.isNull(date)) {
            return null;
        }
        LocalDate monday = getMondayOfWeek(date);
        LocalDate sunday = getSundayOfWeek(date);
        return getDurationDates(monday, sunday);
    }

    /**
     * 获取某日所在月份的每一天的集合
     * @param date 日期
     * @return 1号到最后一号的所有日期集合
     */
    public static List<LocalDate> getDaysOfMonth(LocalDate date) {
        if (Objects.isNull(date)) {
            return null;
        }
        LocalDate firstDayOfMonth = getFirstDayOfMonth(date);
        LocalDate lastDayOfMonth = getLastDayOfMonth(date);
        return getDurationDates(firstDayOfMonth, lastDayOfMonth);
    }

    /**
     * 获取某日临近x天的所有日期集合，比如今天是3号，临近1天，返回2号3号4号的日期集合
     * @param date 日期
     * @param days 临近天数
     * @return 周一到周日的日期集合
     */
    public static List<LocalDate> getNearDays(LocalDate date, Integer days) {
        if (Objects.isNull(date) || Objects.isNull(days) || days == 0) {
            return Collections.EMPTY_LIST;
        }

        LocalDate start = date.plusDays(days);
        LocalDate end = date.minusDays(days);
        return getDurationDates(start, end);
    }

    /**
     * 获取某日所在星期的第一天（获取星期一）
     *
     * @param date 日期
     * @return 星期一
     */
    public static LocalDate getMondayOfWeek(LocalDate date) {
        if (Objects.isNull(date)) {
            return null;
        }
        return date.with(WeekFields.of(DayOfWeek.MONDAY, DayOfWeek.MONDAY.getValue()).dayOfWeek(), MONDAY);
    }

    /**
     * 获取某日所在星期的第一天
     *
     * @param date   日期
     * @param locale 地区
     * @return 星期一
     */
    public static LocalDate getFirstDayOfWeek(LocalDate date, Locale locale) {
        if (Objects.isNull(date) || Objects.isNull(locale)) {
            return null;
        }
        return date.with(WeekFields.of(locale).dayOfWeek(), MONDAY);
    }

    /**
     * 获取某日所在星期的最后一天（获取星期日）
     *
     * @param date 日期
     * @return 星期日
     */
    public static LocalDate getSundayOfWeek(LocalDate date) {
        if (Objects.isNull(date)) {
            return null;
        }
        return date.with(WeekFields.of(DayOfWeek.MONDAY, DayOfWeek.MONDAY.getValue()).dayOfWeek(), SUNDAY);
    }

    /**
     * 获取某日所在星期的最后一天
     *
     * @param date   日期
     * @param locale 地区
     * @return 星期日
     */
    public static LocalDate getLastDayOfWeek(LocalDate date, Locale locale) {
        if (Objects.isNull(date) || Objects.isNull(locale)) {
            return null;
        }
        return date.with(WeekFields.of(locale).dayOfWeek(), SUNDAY);
    }

    /**
     * 获取某日所在月份的第一天
     *
     * @param date
     * @return
     */
    public static LocalDate getFirstDayOfMonth(LocalDate date) {
        if (Objects.isNull(date)) {
            return null;
        }
        return date.with(TemporalAdjusters.firstDayOfMonth());
    }

    /**
     * 获取某日所在月份的最后一天
     *
     * @param date
     * @return
     */
    public static LocalDate getLastDayOfMonth(LocalDate date) {
        if (Objects.isNull(date)) {
            return null;
        }
        return date.with(TemporalAdjusters.lastDayOfMonth());
    }

    /**
     * 获取某日所在年份的第一天
     *
     * @param date
     * @return
     */
    public static LocalDate getFirstDayOfYear(LocalDate date) {
        if (Objects.isNull(date)) {
            return null;
        }
        return date.with(TemporalAdjusters.firstDayOfYear());
    }

    /**
     * 获取某日所在年份的最后一天
     *
     * @param date
     * @return
     */
    public static LocalDate getLastDayOfYear(LocalDate date) {
        if (Objects.isNull(date)) {
            return null;
        }
        return date.with(TemporalAdjusters.lastDayOfYear());
    }

    /**
     * 将日期时间字符串转化为日期时间。
     *
     * @param datetime 日期时间字符串
     * @param pattern  日期时间格式
     * @return 日期时间
     */
    public static LocalDateTime stringToDateTime(String datetime, String pattern) {
        if (StrUtil.isBlank(datetime) || StrUtil.isBlank(pattern)) {
            return null;
        }
        return LocalDateTime.parse(datetime, DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 将日期字符串转化为日期。
     *
     * @param date    日期字符串
     * @param pattern 日期格式
     * @return 日期
     */
    public static LocalDate stringToDate(String date, String pattern) {
        if (StrUtil.isBlank(date) || StrUtil.isBlank(pattern)) {
            return null;
        }
        return LocalDate.parse(date, DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 将日期字符串转化为日期。
     *
     * @param date    日期字符串 8位或10位
     * @return 日期
     */
    public static LocalDate stringToDateSmart(String date) {
        if (StrUtil.isBlank(date)) {
            return null;
        }
        if (date.length() != 8 && date.length() != 10) {
            throw BizException.param();
        }
        String pattern = date.length() == 8 ? DATE_SHORT_PATTERN : DATE_PATTERN;
        return LocalDate.parse(date, DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 将日期时间字符串转化为时间。
     *
     * @param datetime 日期时间字符串
     * @param pattern  日期时间格式
     * @return 时间
     */
    public static LocalTime stringToTime(String datetime, String pattern) {
        if (StrUtil.isBlank(datetime) || StrUtil.isBlank(pattern)) {
            return null;
        }
        return LocalTime.parse(datetime, DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 将日期转化为日期字符串。
     *
     * @param date    日期
     * @param pattern 日期格式
     * @return 日期字符串
     */
    public static String datetimeToString(LocalDateTime date, String pattern) {
        if (Objects.isNull(date) || StrUtil.isBlank(pattern)) {
            return null;
        }
        return DateTimeFormatter.ofPattern(pattern).format(date);
    }

    /**
     * 将日期转化为日期字符串。
     *
     * @param date    日期
     * @param pattern 日期格式
     * @return 日期字符串
     */
    public static String dateToString(LocalDate date, String pattern) {
        if (Objects.isNull(date) || StrUtil.isBlank(pattern)) {
            return null;
        }
        return DateTimeFormatter.ofPattern(pattern).format(date);
    }

    /**
     * 将日期转化为日期字符串。
     *
     * @param date    日期
     * @return 日期字符串yyyy-MM-dd
     */
    public static String toIsoLocalDateString(LocalDate date) {
        if (Objects.isNull(date)) {
            return null;
        }
        return DateTimeFormatter.ISO_LOCAL_DATE.format(date);
    }

    /**
     * 将日期转化为时间字符串。
     *
     * @param time    时间
     * @param pattern 日期格式
     * @return 时间字符串
     */
    public static String timeToString(LocalTime time, String pattern) {
        if (Objects.isNull(time) || StrUtil.isBlank(pattern)) {
            return null;
        }
        return DateTimeFormatter.ofPattern(pattern).format(time);
    }

    /**
     * 获取当前的时分秒
     *
     * @return 当前时间
     */
    public static LocalTime nowHMS() {
        return LocalTime.now();
    }

    /**
     * 获取日期在当年的第几周。
     * 当年第一周规则如下：
     * 如果一年的第一天是星期一，则第一周从1日开始
     * 否则本年第一周开始的几天算属于上一年的末尾周，本年的第一周从下周开始
     * @param date 日期
     * @return 第几周
     */
    public static String getWeekOfYear(LocalDate date) {
        Assert.notNull(date, BizException.param().getMessage());
        LocalDate yearFirstDay = LocalDate.of(date.getYear(), 1, 1);
        // 本年1月1号属于周几
        Integer yearFirstDayOfWeek = getDayOfWeek(yearFirstDay);
        WeekFields weekFields = WeekFields.of(DayOfWeek.MONDAY, 1);
        // 传入日期在本年的周数
        int num = date.get(weekFields.weekOfYear());
        int year = date.getYear();
        //本年1月1号不是周一
        if (yearFirstDayOfWeek != 1) {
            // 传入的日期是本年开始周的日期
            if (num == 1) {
                // 本年的第一周属于去年的最后一周
                year = year - 1;
                num = LocalDate.of(year, 12, 31).get(weekFields.weekOfYear());
            } else {
                num = num - 1;
            }
        }
        return year + "年第" + num + "周";
    }

    /**
     * 开始日期和结束日期的年份差
     * @return 年份差
     */
    public static int getIntervalYear(LocalDate startDate, LocalDate endDate) {
        Assert.notNull(startDate, BizException.param().getMessage());
        Assert.notNull(endDate, BizException.param().getMessage());
        if (startDate.isAfter(endDate)) {
            return startDate.getYear() - endDate.getYear();
        }
        return endDate.getYear() - startDate.getYear();
    }

    /**
     * 开始日期和结束日期的月份差
     * @return 月份差
     */
    public static int getIntervalMonth(LocalDate startDate, LocalDate endDate) {
        Assert.notNull(startDate, BizException.param().getMessage());
        Assert.notNull(endDate, BizException.param().getMessage());
        if (startDate.isAfter(endDate)) {
            //日期交换
            LocalDate temp = startDate;
            startDate = endDate;
            endDate = temp;
        }
        return ((endDate.getYear() - startDate.getYear()) * 12) + endDate.getMonthValue() - startDate.getMonthValue();
    }

    /**
     * 获取开始日期 - 截止日期间隔的天
     *
     * @param startDateTime
     * @param endDateTime
     * @return
     */
    public static int getIntervalDay(LocalDateTime startDateTime, LocalDateTime endDateTime) {
        Assert.notNull(startDateTime, BizException.param().getMessage());
        Assert.notNull(endDateTime, BizException.param().getMessage());
        return getIntervalDay(startDateTime.toLocalDate(), endDateTime.toLocalDate());
    }

    /**
     * 获取开始日期 - 截止日期间隔的天
     *
     * @param startDate
     * @param endDate
     * @return
     */
    public static int getIntervalDay(LocalDate startDate, LocalDate endDate) {
        Assert.notNull(startDate, BizException.param().getMessage());
        Assert.notNull(endDate, BizException.param().getMessage());
        if (startDate.isAfter(endDate)) {
            return Integer.parseInt(ChronoUnit.DAYS.between(endDate, startDate) + "");
        }
        return Integer.parseInt(ChronoUnit.DAYS.between(startDate, endDate) + "");
    }

    /**
     * 获取开始日期 - 截止日期间隔的天
     *
     * @param startDateTime
     * @param endDateTime
     * @return
     */
    public static int getIntervalDay(String startDateTime, String endDateTime) {
        Assert.hasLength(startDateTime, BizException.param().getMessage());
        Assert.hasLength(endDateTime, BizException.param().getMessage());
        return getIntervalDay(stringToDateTime(startDateTime, DATETIME_PATTERN), stringToDateTime(endDateTime, DATETIME_PATTERN));
    }

    /**
     * 获取指定日期的毫秒数时间戳。
     *
     * @param datetime 日期
     * @return
     */
    public static long getMilliTimestamp(LocalDateTime datetime) {
        Assert.notNull(datetime, BizException.param().getMessage());
        return Timestamp.valueOf(datetime).getTime();
    }

    /**
     * 间隔的小时
     *
     * @param startDateTime
     * @param endDateTime
     * @return 间隔分钟/60 保留2位小数
     */
    public static BigDecimal getIntervalHours(LocalDateTime startDateTime, LocalDateTime endDateTime) {
        Assert.notNull(startDateTime, BizException.param().getMessage());
        Assert.notNull(endDateTime, BizException.param().getMessage());
        // 转换成相差的分钟
        return new BigDecimal(getIntervalMinute(startDateTime, endDateTime)).divide(MINUTE_SECONDS, 2, RoundingMode.HALF_UP);
    }

    /**
     * 获取开始日期 - 截止日期间隔的小时
     *
     * @param startDateTime
     * @param endDateTime
     * @return
     */
    public static long getIntervalHour(LocalDateTime startDateTime, LocalDateTime endDateTime) {
        Assert.notNull(startDateTime, BizException.param().getMessage());
        Assert.notNull(endDateTime, BizException.param().getMessage());
        // 转换成相差的分钟
        return (getMilliTimestamp(endDateTime) - getMilliTimestamp(startDateTime)) / (1000 * 60 * 60);
    }

    /**
     * 获取开始时间 - 截止时间间隔的小时
     *
     * @param start
     * @param end
     * @return
     */
    public static BigDecimal getIntervalHour(LocalTime start, LocalTime end) {
        Assert.notNull(start, BizException.param().getMessage());
        Assert.notNull(end, BizException.param().getMessage());
        return new BigDecimal(getIntervalSecond(start, end)).divide(SECONDS_OF_AN_HOUR_BD, 2, RoundingMode.HALF_UP);
    }

    /**
     * 获取开始日期 - 截止日期间隔的小时
     *
     * @param startDateTime
     * @param endDateTime
     * @return
     */
    public static long getIntervalHour(String startDateTime, String endDateTime) {
        Assert.hasLength(startDateTime, BizException.param().getMessage());
        Assert.hasLength(endDateTime, BizException.param().getMessage());
        return getIntervalHour(stringToDateTime(startDateTime, DATETIME_PATTERN),
                stringToDateTime(endDateTime, DATETIME_PATTERN));
    }

    /**
     * 获取开始日期 - 截止日期间隔的分钟
     *
     * @param startDateTime
     * @param endDateTime
     * @return
     */
    public static long getIntervalMinute(LocalDateTime startDateTime, LocalDateTime endDateTime) {
        Assert.notNull(startDateTime, BizException.param().getMessage());
        Assert.notNull(endDateTime, BizException.param().getMessage());
        // 转换成相差的分钟
        return (getMilliTimestamp(endDateTime) - getMilliTimestamp(startDateTime)) / (1000 * 60);
    }

    /**
     * 获取开始日期 - 截止日期间隔的分钟
     *
     * @param startDateTime
     * @param endDateTime
     * @return
     */
    public static long getIntervalMinute(String startDateTime, String endDateTime) {
        Assert.hasLength(startDateTime, BizException.param().getMessage());
        Assert.hasLength(endDateTime, BizException.param().getMessage());
        return getIntervalMinute(stringToDateTime(startDateTime, DATETIME_PATTERN),
                stringToDateTime(endDateTime, DATETIME_PATTERN));
    }

    /**
     * 获取开始时间 - 截止时间间隔的分钟数
     *
     * @param startTime 开始时间
     * @param endTime 截止时间
     * @return 间隔分钟 不足1分钟的按1分钟算
     */
    public static int getIntervalMinute(LocalTime startTime, LocalTime endTime) {
        Assert.notNull(startTime, BizException.param().getMessage());
        Assert.notNull(endTime, BizException.param().getMessage());
        // 转换成相差的分钟
        return new BigDecimal(getIntervalSecond(startTime, endTime)).divide(MINUTE_SECONDS, 0, RoundingMode.UP).intValue();
    }

    /**
     * 获取开始日期 - 截止日期间隔的秒钟
     *
     * @param startDateTime
     * @param endDateTime
     * @return
     */
    public static long getIntervalSecond(LocalDateTime startDateTime, LocalDateTime endDateTime) {
        Assert.notNull(startDateTime, BizException.param().getMessage());
        Assert.notNull(endDateTime, BizException.param().getMessage());
        // 转换成相差的秒数
        return (getMilliTimestamp(endDateTime) - getMilliTimestamp(startDateTime)) / 1000;
    }

    /**
     * 获取开始日期 - 截止日期间隔的秒钟
     *
     * @param startDateTime
     * @param endDateTime
     * @return
     */
    public static long getIntervalSecond(String startDateTime, String endDateTime) {
        Assert.notNull(startDateTime, BizException.param().getMessage());
        Assert.notNull(endDateTime, BizException.param().getMessage());
        return getIntervalSecond(stringToDateTime(startDateTime, DATETIME_PATTERN),
                stringToDateTime(endDateTime, DATETIME_PATTERN));
    }

    /**
     * 获取开始时间 - 截止时间间隔的秒
     *
     * @param startTime 开始时间
     * @param endTime 截止时间
     * @return 间隔秒数
     */
    public static int getIntervalSecond(LocalTime startTime, LocalTime endTime) {
        Assert.notNull(startTime, BizException.param().getMessage());
        Assert.notNull(endTime, BizException.param().getMessage());
        if (startTime.isBefore(endTime)) {
            return endTime.toSecondOfDay() - startTime.toSecondOfDay();
        } else if (startTime.equals(endTime)){
            return SECONDS_OF_DAY_BD.intValue();
        } else if (LocalTime.MIN.equals(endTime)){
            return getIntervalSecond(startTime, LocalTime.MAX) + 1;
        } else {
            return getIntervalSecond(startTime, LocalTime.MAX) + getIntervalSecond(LocalTime.MIN, endTime) + 1;
        }
    }

    /**
     * 计算某年第几周的第几天的日期
     *
     * @param year       年份
     * @param weekOfYear 一年中的第几星期
     * @param dayOfWeek  星期几
     * @return
     */
    public static LocalDate weekDateToDate(Integer year, Integer weekOfYear, DayOfWeek dayOfWeek) {
        if (weekOfYear > 54) {
            weekOfYear = 54;
        }
        WeekFields weekFields = WeekFields.of(DayOfWeek.MONDAY, 1);
        return LocalDate.now()
                .withYear(year)
                .with(weekFields.weekOfYear(), weekOfYear)
                .with(weekFields.dayOfWeek(), dayOfWeek.getValue());
    }

    /**
     * 获取当前日期所属的第几季度
     *
     * @param localDate
     * @return
     */
    public static Integer getQuarter(LocalDate localDate) {
        Integer month = localDate.getMonthValue();
        if (month <= 3) {
            return 1;
        } else if (month <= 6) {
            return 2;
        } else if (month <= 9) {
            return 3;
        }
        return 4;
    }

    /**
     * 根据具体年份周数获取日期范围
     *
     * @param week
     * @return
     */
    public static String getWeekDays(int year, int week, int targetNum) {

        if (week + targetNum > 52) {
            year++;
            week += targetNum - 52;
        } else if (week + targetNum <= 0) {
            year--;
            week += targetNum + 52;
        } else {
            week += targetNum;
        }

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Calendar cal = Calendar.getInstance();

        // 设置每周的开始日期
        cal.setFirstDayOfWeek(Calendar.MONDAY);

        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.WEEK_OF_YEAR, week);

        cal.set(Calendar.DAY_OF_WEEK, cal.getFirstDayOfWeek());
        String beginDate = sdf.format(cal.getTime());

        cal.add(Calendar.DAY_OF_WEEK, 6);
        String endDate = sdf.format(cal.getTime());

        return beginDate + "~" + endDate;
    }

    /**
     * 获取当前时间所在年的周数
     */
    public static Map<String,Integer> getWeekOfYear(Date date) {
        Map<String,Integer> map = new HashMap<>();
        Calendar c = new GregorianCalendar();
        c.setFirstDayOfWeek(Calendar.MONDAY);
        c.setMinimalDaysInFirstWeek(1);
        c.setTime(date);
        int weekYear = c.getWeekYear();
        map.put("weekYear", weekYear);
        map.put("week",c.get(Calendar.WEEK_OF_YEAR));
        return map;
    }

    /**
     * 用小时数转换成天数
     * @param hour 小时数
     * @return 四舍五入2位小数的天数
     */
    public static BigDecimal convertHourToDay(Integer hour) {
        if (Objects.isNull(hour) || hour == 0) {
            return BigDecimal.ZERO;
        }
        return new BigDecimal(hour).divide(HOURS_OF_DAY_BD, 2, RoundingMode.HALF_UP);
    }

    /**
     * 用分钟数转换成天数
     * @param minute 分钟数
     * @return 四舍五入2位小数的天数
     */
    public static BigDecimal convertMinuteToDay(Integer minute) {
        if (Objects.isNull(minute) || minute == 0) {
            return BigDecimal.ZERO;
        }
        return new BigDecimal(minute).divide(MINUTES_OF_DAY_BD, 2, RoundingMode.HALF_UP);
    }

    /**
     * 用秒数转换成小天数
     * @param second 秒
     * @return 四舍五入2位小数的天数
     */
    public static BigDecimal convertSecondToDay(Integer second) {
        if (Objects.isNull(second) || second == 0) {
            return BigDecimal.ZERO;
        }
        return new BigDecimal(second).divide(SECONDS_OF_DAY_BD, 2, RoundingMode.HALF_UP);
    }

    /**
     * 用分钟数转换成小时数
     * @param minute 分钟数
     * @return 四舍五入2位小数的小时数
     */
    public static BigDecimal convertMinuteToHour(Integer minute) {
        if (Objects.isNull(minute) || minute == 0) {
            return BigDecimal.ZERO;
        }
        return new BigDecimal(minute).divide(MINUTE_SECONDS, 2, RoundingMode.HALF_UP);
    }

    /**
     * 用秒数转换成小时数
     * @param second 秒
     * @return 四舍五入2位小数的小时数
     */
    public static BigDecimal convertSecondToHour(Integer second) {
        if (Objects.isNull(second) || second == 0) {
            return BigDecimal.ZERO;
        }
        return new BigDecimal(second).divide(SECONDS_OF_AN_HOUR_BD, 2, RoundingMode.HALF_UP);
    }

    /**
     * 目标时间在开始结束之间  start <= time <= end
     * @param time 目标时间
     * @param start 开始
     * @param end 结束
     * @return true在  false不在
     */
    public static boolean between(LocalDateTime time, LocalDateTime start, LocalDateTime end) {
        if (ObjectUtil.hasNull(time, start, end)) {
            return false;
        }
        return !time.isBefore(start) && !time.isAfter(end);
    }

    /**
     * 目标日期在开始结束之间  start <= time <= end
     * @param date 目标日期
     * @param start 开始
     * @param end 结束
     * @return true在  false不在
     */
    public static boolean between(LocalDate date, LocalDate start, LocalDate end) {
        if (ObjectUtil.hasNull(date, start, end)) {
            return false;
        }
        return !date.isBefore(start) && !date.isAfter(end);
    }

    /**
     * 将LocalDate 转 Date
     * @param localDate
     * @return
     */
    public static Date convert2Date(LocalDate localDate) {
        LocalDateTime localDateTime = localDate.atStartOfDay();
        Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
        return  Date.from(instant);
    }

}


